package com.why.cachecloud.service.stats.admin.impl;

import com.why.cachecloud.service.custom.alert.DingTalkComponent;
import com.why.cachecloud.dao.AppDao;
import com.why.cachecloud.dao.ResourceDao;
import com.why.cachecloud.entity.AppDesc;
import com.why.cachecloud.entity.SystemResource;
import com.why.cachecloud.service.machine.MachineCenter;
import com.why.cachecloud.service.stats.admin.CoreAppsStatCenter;
import com.why.cachecloud.util.ConstUtils;
import com.why.cachecloud.util.StringUtil;
import com.why.cachecloud.web.service.AppService;
import com.why.cachecloud.web.service.UserService;
import com.why.cachecloud.web.util.DateUtil;
import com.why.cachecloud.web.vo.MachineEnv;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Created by why.developer on 2020/3/25
 */
@Service
@Slf4j
public class CoreAppsStatCenterImpl implements CoreAppsStatCenter {

    @Autowired
    private AppService appService;
    @Autowired
    private AppDao appDao;
    @Autowired
    private UserService userService;
    @Autowired
    private ResourceDao resourceDao;
    @Autowired
    private DingTalkComponent dingTalkComponent;
    @Autowired
    private MachineCenter machineCenter;

    @Override
    public boolean sendExpAppsStatDataEmail(String searchDate) {
        try {
            log.info("sendExpAppsStatDataEmail");
            if (StringUtil.isBlank(searchDate)) {
                //默认发送前一天的日报
                Date startDate = DateUtils.addDays(new Date(), -1);
                searchDate = DateUtil.formatDate(startDate, "yyyy-MM-dd");
            }
            List<AppDesc> appDescList = appDao.getOnlineAppsNonTest();
            // 符合条件（已经审批发布并且不是测试）的应用为空直接返回true
            if (CollectionUtils.isEmpty(appDescList)) {
                return true;
            }
            appDescList.forEach(appDesc -> {
                String versionName = Optional.ofNullable(resourceDao.getResourceById(appDesc.getVersionId())).map(SystemResource::getName).orElse("");
                appDesc.setVersionName(versionName);
                appDesc.setOfficer(userService.getOfficerName(appDesc.getOfficer()));
            });
            Map<String, AppDesc> appDescMap = appDescList.stream().collect(Collectors.toMap(appDesc -> String.valueOf(appDesc.getAppId()), Function.identity()));


            Map<String, List<Map<String, Object>>> appClientGatherStatGroup = appService.getFilterAppClientStatGather(searchDate);

            // 获取异常的宿主或容器信息
            Map<String, Object> exceptionMachineEnv = machineCenter.getExceptionMachineEnv(DateUtil.parseYYYY_MM_dd(searchDate));

            noticeExpAppsDaily(searchDate, appDescMap, appClientGatherStatGroup, exceptionMachineEnv);
            return true;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return false;
        }
    }

    public void noticeExpAppsDaily(String searchDate, Map<String, AppDesc> appDescMap, Map<String, List<Map<String, Object>>> appClientGatherStatGroup, Map<String, Object> exceptionMachineEnv) {
        String title = String.format("【CacheCloud】%s应用日报 ", searchDate);
        StringBuilder titleBuilder = new StringBuilder();
        titleBuilder.append(title).append(ConstUtils.getPhoneString()).append(" \n\n");
        StringBuilder sb = new StringBuilder();
        List<Map<String, Object>> expAppStats = appClientGatherStatGroup.get("expAppStats");
        if (!CollectionUtils.isEmpty(expAppStats)) {
            sb.append("**应用异常情况：**").append(" \n\n");
            for (Map<String, Object> item : expAppStats) {
                Long appId = (Long) item.get("app_id");
                AppDesc appDesc = appDescMap.get(String.valueOf(appId));
                String name = appDesc.getName();
                String officer = appDesc.getOfficer();
                sb.append("--- \n\n");
                sb.append("应用id：").append(appId).append(" \n\n");
                sb.append("应用名：").append(name).append(" \n\n");
                sb.append("应用负责人：").append(officer).append(" \n\n");
                sb.append("异常总量(全天)：").append(item.get("exp_count")).append(" \n\n");
                sb.append("连接异常(全天)：").append(" \n\n")
                        .append("- 次数：").append(item.get("conn_exp_count")).append(" \n\n")
                        .append("- 平均耗时(ms)：").append(item.get("avg_conn_exp_cost")).append(" \n\n");
                sb.append("命令超时(全天)：").append(" \n\n")
                        .append("- 次数：").append(item.get("cmd_exp_count")).append(" \n\n")
                        .append("- 平均耗时(ms)：").append(item.get("avg_cmd_exp_cost")).append(" \n\n");
                sb.append("延迟事件(全天)：").append(item.get("latency_count")).append(" \n\n");
                sb.append("慢查询(全天)：").append(item.get("slow_log_count")).append(" \n\n");
            }

        }


        List<Map<String, Object>> latencyAppStats = appClientGatherStatGroup.get("latencyAppStats");
        if (!CollectionUtils.isEmpty(latencyAppStats)) {
            sb.append("**应用延迟事件（top10）：**").append(" \n\n");
            for (Map<String, Object> item : latencyAppStats) {
                Long appId = (Long) item.get("app_id");
                AppDesc appDesc = appDescMap.get(String.valueOf(appId));
                String name = appDesc.getName();
                String officer = appDesc.getOfficer();
                sb.append("--- \n\n");
                sb.append("应用id：").append(appId).append(" \n\n");
                sb.append("应用名：").append(name).append(" \n\n");
                sb.append("应用负责人：").append(officer).append(" \n\n");
                sb.append("延迟事件(全天)：").append(item.get("latency_count")).append(" \n\n");
                sb.append("慢查询(全天)：").append(item.get("slow_log_count")).append(" \n\n");
            }
        }


        List<Map<String, Object>> topologyAppStats = appClientGatherStatGroup.get("topologyAppStats");
        if (!CollectionUtils.isEmpty(topologyAppStats)) {
            sb.append("**应用拓扑诊断：**").append(" \n\n");
            for (Map<String, Object> item : topologyAppStats) {
                Long appId = (Long) item.get("app_id");
                AppDesc appDesc = appDescMap.get(String.valueOf(appId));
                String name = appDesc.getName();
                String officer = appDesc.getOfficer();
                String typeDesc = appDesc.getTypeDesc();
                String versionName = appDesc.getVersionName();
                sb.append("--- \n\n");
                sb.append("应用id：").append(appId).append(" \n\n");
                sb.append("应用名：").append(name).append(" \n\n");
                sb.append("应用负责人：").append(officer).append(" \n\n");
                sb.append("redis类型：").append(typeDesc).append(" \n\n");
                sb.append("redis版本：").append(versionName).append(" \n\n");
            }

        }


        List<Map<String, Object>> machineEnvs = (List<Map<String, Object>>) exceptionMachineEnv.get("host");
        if (!CollectionUtils.isEmpty(machineEnvs)) {
            sb.append("**宿主环境检测：**").append(" \n\n");
            for (Map<String, Object> machine : machineEnvs) {
                sb.append("--- \n\n");
                MachineEnv envs = (MachineEnv) machine.get("envs");
                sb.append("宿主机ip：").append(machine.get("ip")).append(" \n\n");
                sb.append("cachecloud_nprocs：").append(envs.getNproc_threads()).append(" \n\n");
                sb.append("fsync_slow_times：").append(envs.getFsync_delay_times()).append(" \n\n");
                sb.append("somaxconn：").append(envs.getSomaxconn()).append(" \n\n");
                sb.append("redis实例数：").append(envs.getInstanceNum()).append(" \n\n");
                sb.append("文件句柄used/total：").append(envs.getUnlimit_used()).append("/").append(envs.getUnlimit()).append(" \n\n");
                sb.append("disk used：").append(envs.getDiskUsed()).append(" \n\n");
            }
        }


        List<Map<String, Object>> containers = (List<Map<String, Object>>) exceptionMachineEnv.get("container");
        if (!CollectionUtils.isEmpty(containers)) {
            sb.append("**容器环境检测：**").append(" \n\n");
            for (Map<String, Object> container : containers) {
                sb.append("--- \n\n");
                MachineEnv envs = (MachineEnv) container.get("envs");
                sb.append("容器ip：").append(container.get("ip")).append(" \n\n");
                sb.append("overcommit_memory：").append(envs.getOvercommit_memory()).append(" \n\n");
                sb.append("thp_enabled：").append(envs.getTransparent_hugepage_enable()).append(" \n\n");
                sb.append("thp_defrag：").append(envs.getTransparent_hugepage_defrag()).append(" \n\n");
                sb.append("swappiness：").append(envs.getSwappines()).append(" \n\n");
                sb.append("nproc：").append(envs.getNproc()).append(" \n\n");
            }

        }


        List<Map<String, Object>> memAlterAppStats = appClientGatherStatGroup.get("memAlterAppStats");
        if (!CollectionUtils.isEmpty(memAlterAppStats)) {
            sb.append("**应用内存使用情况（bottom 10）：**").append(" \n\n");
            for (Map<String, Object> item : memAlterAppStats) {
                sb.append("--- \n\n");
                Long appId = (Long) item.get("app_id");
                AppDesc appDesc = appDescMap.get(String.valueOf(appId));
                sb.append("应用id：").append(appId).append(" \n\n");
                sb.append("应用名：").append(appDesc.getName()).append(" \n\n");
                sb.append("应用负责人：").append(appDesc.getOfficer()).append(" \n\n");
                sb.append("redis类型：").append(appDesc.getTypeDesc()).append(" \n\n");
                sb.append("内存：").append(item.get("format_mem")).append(" G").append(" \n\n");
                sb.append("使用内存：").append(item.get("format_used_memory")).append(" \n\n");
                sb.append("内存使用率：").append(item.get("mem_used_ratio")).append(" \n\n");
                sb.append("应用客户端连接：").append(item.get("connected_clients")).append(" \n\n");
            }
        }



        List<Map<String, Object>> fragRatioAppStats = appClientGatherStatGroup.get("fragRatioAppStats");
        if (!CollectionUtils.isEmpty(fragRatioAppStats)) {
            sb.append("**应用碎片率情况（top 10）：**").append(" \n\n");
            for (Map<String, Object> item : fragRatioAppStats) {
                sb.append("--- \n\n");
                Long appId = (Long) item.get("app_id");
                AppDesc appDesc = appDescMap.get(String.valueOf(appId));
                sb.append("应用id：").append(appId).append(" \n\n");
                sb.append("应用名：").append(appDesc.getName()).append(" \n\n");
                sb.append("应用负责人：").append(appDesc.getOfficer()).append(" \n\n");
                sb.append("redis版本：").append(appDesc.getVersionName()).append(" \n\n");
                sb.append("内存使用：").append(item.get("format_used_memory")).append(" G").append(" \n\n");
                sb.append("rss内存使用：").append(item.get("format_used_memory_rss")).append(" \n\n");
                sb.append("平均碎片率：").append(item.get("avg_mem_frag_ratio")).append(" \n\n");
            }
        }

        if (StringUtils.isNotBlank(sb.toString())) {
            titleBuilder.append(sb);
            dingTalkComponent.sendDingTalkToAdmin(titleBuilder.toString());
            log.info("noticeExpAppsDaily success");
        }

    }


}
